﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Collections.ObjectModel;
using Microsoft.Scripting.Hosting;
using Flame.Reflection;

namespace Flame.Dlr
{


    public class PowerShellNoComponentException : FlameException
    {
        public PowerShellNoComponentException(Exception exception)
            : base(Flame.Dlr.Powershell.Properties.Resources.PowerShellNoComponent, exception)
        {
        }
    }
    public class PowerShellExec : IExecutable
    {
        Runspace _runspace;
        List<Variable> _vars;
        Exception _exception;
        public PowerShellExec()
        {
            _vars = new List<Variable>();
            ClearScope(null);
            _runspace = null;

            try
            {
                _runspace = RunspaceFactory.CreateRunspace();
                _runspace.Open();

                // http://msdn.microsoft.com/en-us/library/system.management.automation.runspaces.runspace.defaultrunspace(v=vs.85).aspx
                // A typically scenario where the default runspace is used is when a runspace has created a PSObject object that has script methods. 
                // If the original runspace is destroyed while a program still has a handle to the PSObject object, the Windows PowerShell runtime 
                // uses the default runspace specified by this property to invoke the script, even though the original runspace was destroyed.
                System.Management.Automation.Runspaces.Runspace.DefaultRunspace = _runspace;
            }
            catch (Exception e)
            {
                _exception = e;
            }
        }
        public Result Execute(string text)
        {
            if (_runspace == null) return new Result() { Data = new PowerShellNoComponentException(_exception) { } };
            Pipeline pipeline = _runspace.CreatePipeline();
            foreach (Variable v in _vars)
            {
                pipeline.Runspace.SessionStateProxy.SetVariable(v.Name, v.Data);
            }

            dynamic d;
            try
            {
                pipeline.Commands.AddScript(text);
                d = pipeline.Invoke();
            }
            catch (Exception e)
            {
                d = e;
            }

            return new Result() { Data = d };
        }

        ~PowerShellExec()
        {
            if (_runspace != null)
                _runspace.Close();
        }

        public void AddVariable(Variable variable)
        {
            _vars.Add(variable);
        }

        public void ClearScope(ScopeParameters scopeParameters)
        {
            _vars.Clear();
            if (scopeParameters != null) scopeParameters.Apply(this);
        }


        public ResultAssembly AddAssembly(AssemblyWrapper assembly)
        {
            Exception e_ = null;
            try
            {
                Execute("Add-Type -Path " + assembly.Location);
            }
            catch (Exception e)
            {
                e_ = e;
            }
            return new ResultAssembly() { Exception = e_, Loaded = e_ == null, Language = GetType() };
        }
        public IIntellisense Intellisense { get { return null; } }
    }
}
